/*************************************************************************
    > File Name: util.c
    > Author: liujing
    > Mail: jingliu.cs@gmail.com 
    > Created Time: 2014年10月17日 星期五 20时48分07秒
 ************************************************************************/

#include "globals.h"
#include "util.h"
#include "scan.h"


typedef enum{ 
	START,
    INASSIGN,INEQ,
	ENTERCOMMENT,INCOMMENT,EXISTCOMMENT,
	INOVER,
	INNUM,INID,
	INLT,INLTE,
	INGT,INGTE,
	INNEQ,
	DONE}StateType;

//lexeme
char tokenString[MAXTOKENLEN+1];
//input buffer length
#define BUFLEN 1024

static char lineBuf[BUFLEN]; 
static int linepos = 0;
static int bufsize = 0; 
static int EOF_flag = FALSE;

static int getNextChar(void)
{ if (!(linepos < bufsize))
  { lineno++;
    if (fgets(lineBuf,BUFLEN-1,source))
    { if (EchoSource) fprintf(listing,"%4d: %s",lineno,lineBuf);
      bufsize = strlen(lineBuf);
      linepos = 0;
      return lineBuf[linepos++];
    }
    else
    { EOF_flag = TRUE;
      return EOF;
    }
  }
  else return lineBuf[linepos++];
}

//put the last char back
static void ungetNextChar(void)
{ if (!EOF_flag) linepos-- ;}

//look up reserved words
static struct
    { char* str;
      TokenType tok;
    } reservedWords[MAXRESERVED]
   = {{"if",IF},{"while",WHILE},{"else",ELSE},{"void",VOID},
      {"return",RETURN},{"int",INT}};

//ookup an identifier to see if it is a reserved word 
// uses linear search 
static TokenType reservedLookup (char* s){ 
	int i;
	for(i=0;i<MAXRESERVED;i++)
		if(!strcmp(s,reservedWords[i].str)){
			return reservedWords[i].tok;
		}
	return ID;
}

TokenType getToken(void){ 
	int tokenStringIndex = 0;
	TokenType currentToken;
	StateType state = START;
	int save;	//flag if save=1, then save as token
	while(state!=DONE){ 
		int c=getNextChar();
		save=TRUE;
		switch(state){ 
			case START:
				if(isdigit(c))
					state = INNUM;
				else if (isalpha(c))
					state = INID;
				else if ((c == ' ') || (c == '\t') || (c == '\n'))
					save = FALSE;
				else if(c=='/'){
					save=FALSE;
					state=ENTERCOMMENT;
				}
				else if(c=='='){
					save=FALSE;
					state=INEQ;
				}
				else if(c=='<'){
					save=FALSE;
					state=INLTE;
				}
				else if(c=='>'){
					save=FALSE;
					state=INGTE;
				}
				else if(c=='!'){
					save=FALSE;
					state=INNEQ;
				}
				else{ 
					state = DONE;
					switch (c){
						case EOF:
							save = FALSE;
							currentToken = ENDFILE;
							break;
						case '=':
							currentToken = EQ;
							break;
						case '+':
							currentToken = PLUS;
							break;
						case '-':
							currentToken = MINUS;
							break;
						case '*':
							currentToken = TIMES;
							break;
						case '/':
							currentToken = OVER;
							break;
						case '(':
							currentToken = LPAREN;
							break;
						case ')':
							currentToken = RPAREN;
							break;
						case '[':
							currentToken=LSQUARE;
							break;
						case ']':
							currentToken=RSQUARE;
							break;
						case '{':
							currentToken=LBRACE;
							break;
						case '}':
							currentToken=RBRACE;
							break;
						case ';':
							currentToken = SEMI;
							break;
						case ',':
							currentToken=COMMA;
							break;
						default:
							currentToken = ERROR;
							break;
					}
				}
				break;
			case ENTERCOMMENT:
				//printf("Enter Comments\n");
				save=FALSE;
				if(c=='*'){
					state=INCOMMENT;
				}else{
					ungetNextChar();
					ungetNextChar();
					state=INOVER;
				}
				break;
			case INCOMMENT:
				save=FALSE;
				if(c=='*'){
					state=EXISTCOMMENT;
				}else if(c==EOF){
					state=DONE;
					currentToken=ENDFILE;
				}
				break;
			case EXISTCOMMENT:
				save=FALSE;
				if(c=='/'){
					state=START;
				}else{
					state=INCOMMENT;
				}
				break;
			case INOVER:
				if(c=='/'){
					state=DONE;
					currentToken=OVER;
				}
				break;
			case INEQ:
				if(c=='='){
					state=DONE;
					currentToken=EQ;
				}else{
					save=FALSE;
					state=INASSIGN;
					ungetNextChar();
					ungetNextChar();
				}
				break;
			case INASSIGN:
				state = DONE;
				currentToken=ASSIGN;
				break;
			case INLTE:
				if(c=='='){
					state=DONE;
					currentToken=LTE;
				}else{
					save=FALSE;
					state=INLT;
					ungetNextChar();
					ungetNextChar();
				}
				break;
			case INLT:
				state=DONE;
				currentToken=LT;
				break;
			case INGT:
				state=DONE;
				currentToken=GT;
				break;
			case INGTE:
				if(c=='='){
					state=DONE;
					currentToken=GTE;
				}else{
					save=FALSE;
					state=INGT;
					ungetNextChar();
					ungetNextChar();
				}
				break;
			case INNEQ:
				state=DONE;
				if(c=='='){
					currentToken=NEQ;
				}else{
					save=FALSE;
					currentToken=ERROR;
					ungetNextChar();
				}
				break;
			case INNUM:
				if (!isdigit(c)){ 
					ungetNextChar();
					save = FALSE;
					state = DONE;
					currentToken = NUM;
				}
				break;
			case INID:
				if (!(isalpha(c)||isdigit(c))){
					ungetNextChar();
					save = FALSE;
					state = DONE;
					currentToken = ID;
				}
				break;
			case DONE:
			default: 
				fprintf(listing,"Some Bug: state= %d\n",state);
				state = DONE;
				currentToken = ERROR;
				break;
	}
    if((save)&&(tokenStringIndex<=MAXTOKENLEN))
		tokenString[tokenStringIndex++] = (char) c;
	if(state==DONE){ 
		tokenString[tokenStringIndex] = '\0';
		if(currentToken==ID)
			currentToken = reservedLookup(tokenString);
		}
	}
	if(TraceScan){
		fprintf(listing,"\t%d: ",lineno);
		printToken(currentToken,tokenString);
	}
	return currentToken;
}

